/* Copyright 2011-2016 Google Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package com.google.security.zynamics.zylib.gui.dndtree; import java.awt.AlphaComposite; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.image.BufferedImage; import java.io.IOException; import javax.swing.JComponent; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; public abstract class AbstractTreeTransferHandler implements DragGestureListener, DragSourceListener, DropTargetListener { private static DefaultMutableTreeNode draggedNode; private static BufferedImage image = null; // buff image private final DNDTree tree; private final DragSource dragSource; // dragsource private final DropTarget dropTarget; // droptarget private DefaultMutableTreeNode draggedNodeParent; private final Rectangle rect2D = new Rectangle(); private final boolean drawImage; protected AbstractTreeTransferHandler(final DNDTree tree, final int action, final boolean drawIcon) { this.tree = tree; drawImage = drawIcon; dragSource = new DragSource(); dragSource.createDefaultDragGestureRecognizer(tree, action, this); dropTarget = new DropTarget(tree, action, this); } private final void clearImage() { tree.paintImmediately(rect2D.getBounds()); } private final void paintImage(final Point pt) { if (image != null) { tree.paintImmediately(rect2D.getBounds()); rect2D.setRect((int) pt.getX(), (int) pt.getY(), image.getWidth(), image.getHeight()); tree.getGraphics().drawImage(image, (int) pt.getX(), (int) pt.getY(), tree); } } protected abstract boolean canPerformAction(DNDTree tree, DataFlavor flavor, Transferable tranferable, int action, Point pt); protected abstract boolean executeDrop(DNDTree tree, Transferable transferable, DefaultMutableTreeNode newParentNode, int action); public abstract boolean canPerformAction(DNDTree target, DefaultMutableTreeNode draggedNode, int action, Point location); /* Methods for DragSourceListener */ @Override public void dragDropEnd(final DragSourceDropEvent dsde) { if (dsde.getDropSuccess() && (dsde.getDropAction() == DnDConstants.ACTION_MOVE) && (draggedNodeParent != null)) { ((DefaultTreeModel) tree.getModel()).nodeStructureChanged(draggedNodeParent); } } /* Methods for DropTargetListener */ @Override public final void dragEnter(final DragSourceDragEvent dsde) { final int action = dsde.getDropAction(); if (action == DnDConstants.ACTION_COPY) { dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop); } else { if (action == DnDConstants.ACTION_MOVE) { dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop); } else { dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop); } } } @Override public final void dragEnter(final DropTargetDragEvent dtde) { final Point pt = dtde.getLocation(); final int action = dtde.getDropAction(); if (drawImage) { paintImage(pt); } final Transferable transferable = dtde.getTransferable(); if (!transferable.isDataFlavorSupported(TransferableNode.NODE_FLAVOR)) { if (canPerformAction(tree, dtde.getCurrentDataFlavorsAsList().get(0), dtde.getTransferable(), action, pt)) { dtde.acceptDrag(action); } else { dtde.rejectDrag(); } } else { if (canPerformAction(tree, draggedNode, action, pt)) { dtde.acceptDrag(action); } else { dtde.rejectDrag(); } } } @Override public final void dragExit(final DragSourceEvent dse) { dse.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop); } @Override public final void dragExit(final DropTargetEvent dte) { if (drawImage) { clearImage(); } } /* Methods for DragGestureListener */ @Override public final void dragGestureRecognized(final DragGestureEvent dge) { // final TreePath path = tree.getSelectionPath(); final TreePath path = tree.getPathForLocation(dge.getDragOrigin().x, dge.getDragOrigin().y); if (path != null) { draggedNode = (DefaultMutableTreeNode) path.getLastPathComponent(); draggedNodeParent = (DefaultMutableTreeNode) draggedNode.getParent(); if (drawImage) { final Rectangle pathBounds = tree.getPathBounds(path); // getpathbounds of selectionpath final JComponent lbl = (JComponent) tree.getCellRenderer().getTreeCellRendererComponent(tree, draggedNode, false, tree.isExpanded(path), ((DefaultTreeModel) tree.getModel()).isLeaf(path.getLastPathComponent()), 0, false);// returning // the // label lbl.setBounds(pathBounds);// setting bounds to lbl image = new BufferedImage(lbl.getWidth(), lbl.getHeight(), java.awt.image.BufferedImage.TYPE_INT_ARGB_PRE);// buffered image reference passing // the label's ht and width final Graphics2D graphics = image.createGraphics();// creating the graphics for buffered // image graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); // Sets // the // Composite // for the // Graphics2D // context lbl.setOpaque(false); lbl.paint(graphics); // painting the graphics to label graphics.dispose(); } dragSource.startDrag(dge, DragSource.DefaultMoveNoDrop, image, new Point(0, 0), new TransferableNode(draggedNode), this); } } @Override public final void dragOver(final DragSourceDragEvent dsde) { final int action = dsde.getDropAction(); if (action == DnDConstants.ACTION_COPY) { dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop); } else { if (action == DnDConstants.ACTION_MOVE) { dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop); } else { dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop); } } } @Override public final void dragOver(final DropTargetDragEvent dtde) { final Point pt = dtde.getLocation(); final int action = dtde.getDropAction(); tree.autoscroll(pt); if (drawImage) { paintImage(pt); } final Transferable transferable = dtde.getTransferable(); if (!transferable.isDataFlavorSupported(TransferableNode.NODE_FLAVOR)) { if (canPerformAction(tree, dtde.getCurrentDataFlavorsAsList().get(0), dtde.getTransferable(), action, pt)) { dtde.acceptDrag(action); } else { dtde.rejectDrag(); } } else { if (canPerformAction(tree, draggedNode, action, pt)) { dtde.acceptDrag(action); } else { dtde.rejectDrag(); } } } @Override public final void drop(final DropTargetDropEvent dtde) { if (drawImage) { clearImage(); } final int action = dtde.getDropAction(); final Transferable transferable = dtde.getTransferable(); final Point pt = dtde.getLocation(); if (transferable.isDataFlavorSupported(TransferableNode.NODE_FLAVOR) && canPerformAction(tree, draggedNode, action, pt)) { boolean gotData = false; DefaultMutableTreeNode node = null; try { node = (DefaultMutableTreeNode) transferable.getTransferData(TransferableNode.NODE_FLAVOR); gotData = true; } catch (final IOException e) { // TODO: This should be properly logged System.out.println(e); } catch (final UnsupportedFlavorException e) { // TODO: This should be properly logged System.out.println(e); } if (gotData) { final TreePath pathTarget = tree.getPathForLocation(pt.x, pt.y); final DefaultMutableTreeNode newParentNode = (DefaultMutableTreeNode) pathTarget.getLastPathComponent(); if (executeDrop(tree, node, newParentNode, action)) { dtde.acceptDrop(action); dtde.dropComplete(true); return; } } } else if (canPerformAction(tree, dtde.getCurrentDataFlavors()[0], dtde.getTransferable(), action, pt)) { final TreePath pathTarget = tree.getPathForLocation(pt.x, pt.y); final DefaultMutableTreeNode newParentNode = (DefaultMutableTreeNode) pathTarget.getLastPathComponent(); if (executeDrop(tree, dtde.getTransferable(), newParentNode, action)) { dtde.acceptDrop(action); dtde.dropComplete(true); return; } } dtde.rejectDrop(); dtde.dropComplete(false); } @Override public final void dropActionChanged(final DragSourceDragEvent dsde) { final int action = dsde.getDropAction(); if (action == DnDConstants.ACTION_COPY) { dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop); } else { if (action == DnDConstants.ACTION_MOVE) { dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop); } else { dsde.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop); } } } @Override public final void dropActionChanged(final DropTargetDragEvent dtde) { final Point pt = dtde.getLocation(); final int action = dtde.getDropAction(); if (drawImage) { paintImage(pt); } if (draggedNode == null) { if (canPerformAction(tree, dtde.getCurrentDataFlavorsAsList().get(0), dtde.getTransferable(), action, pt)) { dtde.acceptDrag(action); } else { dtde.rejectDrag(); } } else { if (canPerformAction(tree, draggedNode, action, pt)) { dtde.acceptDrag(action); } else { dtde.rejectDrag(); } } } public abstract boolean executeDrop(DNDTree tree, DefaultMutableTreeNode draggedNode, DefaultMutableTreeNode newParentNode, int action); public DropTarget getDropTarget() { return dropTarget; } }